package com.test.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class NioSocketServer {
    private String ip;
    private int port;
    private Map<String, SocketChannel> clients;//可以是ip+端口,value=通道
    private ByteBuffer readBuffer;
    private ByteBuffer writeBuffer;
    private Selector selector;

    public NioSocketServer() {
        this("localhost", 8111, 512);
    }

    public NioSocketServer(String ip, int port, int bufferLength) {
        this.ip = ip;
        this.port = port;
        this.readBuffer = ByteBuffer.allocate(bufferLength);
        this.writeBuffer = ByteBuffer.allocate(bufferLength);
        this.clients = new TreeMap<>();
        initServer();
    }

    //初始化
    private void initServer() {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //打开网络通道
            serverSocketChannel.configureBlocking(false);//设置通道是非阻塞
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(ip, port));//绑定ip和端口
            selector = Selector.open();
            //将指定的通道注册到选择其中并指定监听事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端已经启动=" + ip + ":" + port);
            handlerListen();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //事件监听
    private void handlerListen() {
        try {
            while (true) {
                selector.select();//获取事件，如果没有就阻塞
                Set<SelectionKey> keys = selector.selectedKeys();
                for (SelectionKey key : keys) {
                    handler(key);//处理事件
                }
                keys.clear();//清空，否则下一次迭代中还会存在本次的SelectionKey
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //处理事件
    public void handler(SelectionKey key) {
        ServerSocketChannel server;
        SocketChannel socketChannel;
        try {
            if (key.isAcceptable()) {//有连接了
                server = (ServerSocketChannel) key.channel();
                socketChannel = server.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);//监听读取事件
                String clientKey = socketChannel.socket().getInetAddress().getHostAddress() + ":" + socketChannel.socket().getPort();
                if (!clients.containsKey(clientKey)) {
                    clients.put(clientKey, socketChannel);
                }
            } else if (key.isReadable()) {//可以读取，客户端给服务端发消息了
                socketChannel = (SocketChannel) key.channel();
                readBuffer.clear();
                int len = socketChannel.read(readBuffer);
                readBuffer.flip();
                System.out.println("可读取：" + new String(readBuffer.array(), 0, len));
                if (len > 0) {
                    String name = socketChannel.socket().getInetAddress().getHostAddress() + ":" + socketChannel.socket().getPort();
                    for (String clientKey : clients.keySet()) {
                        if (name.equals(clientKey)) {//不推送给自己
                            continue;
                        }
                        //逐一发送，把消息发送给聊天室其它人
                        SocketChannel channel = clients.get(clientKey);
                        writeBuffer.clear();
                        writeBuffer.put(name.getBytes());
                        writeBuffer.put("说：".getBytes());
                        writeBuffer.put(readBuffer);
                        writeBuffer.flip();
                        channel.write(writeBuffer);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
